﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using InfoJetSoft.Service.Entity;
using InfoJetSoft.Service.Util;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Security.Principal;
using System.Security.Cryptography.X509Certificates;

//CID 0749 //2011.09.01 //Begin
namespace InfoJetSoft.Service.Util
{
    class DigitalSignatureChecker
    {
        public static void CheckSignature(Form form)
        {
            if (ConfigUtil.EnableDigitalSignature())
            {
                if (form.Manifest.SignedDataBlockList != null)
                {
                    form.SignedDataInfoList = new ArrayList();
                    foreach (SignedDataBlock block in form.Manifest.SignedDataBlockList)
                    {
                        XmlElement containerElement = DOMUtil.selectANode(form.XmlDoc.DocumentElement, block.Location, form.XmlNsResolver) as XmlElement;
                        if (containerElement != null)
                        {
                            SignedDataInfo info = new SignedDataInfo();
                            info.Name = block.Name;
                            info.Mode = block.Mode;
                            info.ContainerElement = containerElement;
                            List<XmlElement> signElementList = QuickSelectUtil.SelectChildElementsByLocalName(containerElement, "Signature");
                            info.SignatureElementList = new List<SignedElementInfo>();
                            if (signElementList.Count > 0)
                            {
                                if (block.Mode.Equals("single") && signElementList.Count > 1)
                                {
                                    info.HasExtraSignature = true;
                                    //CID 1552 //2013.01.11 //Begin
                                    if (form.Command.Equals(JetConstant.CMD_BUILD_BY_XML))
                                    {
                                        if (form.ModalMessageList == null)
                                        {
                                            form.ModalMessageList = new ArrayList();
                                        }
                                        //CID 1030 //2012.01.05 //Begin
                                        form.ModalMessageList.Add("Only one signature is allowed for the block " + block.Name + ".");
                                        //CID 1030 //2012.01.05 //End
                                    }
                                    //CID 1552 //2013.01.11 //End
                                }
                                else
                                {
                                    foreach (XmlElement signElement in signElementList)
                                    {
                                        SignedElementInfo elementInfo = CheckSignature(signElement, info.Mode);
                                        info.SignatureElementList.Add(elementInfo);
                                    }
                                }
                            }
                            form.SignedDataInfoList.Add(info);
                        }
                    }
                }
            }
        }

        public static SignedStatus CheckSignature(Form form, XmlElement signElement, string mode)
        {
            //CID 1021 //2011.09.29 //Begin
            JetIdCache idCache = new JetIdCache(form.XmlDoc.DocumentElement);
            SignedElementInfo elementInfo = CheckSignature(signElement, mode);
            idCache.reset();
            return elementInfo.Status;
            //CID 1021 //2011.09.29 //End
        }

        //CID 1021 //2011.09.29 //Begin
        private static SignedElementInfo CheckSignature(XmlElement signElement, string mode)
        {
            SignedElementInfo elementInfo = new SignedElementInfo();
            elementInfo.SignatureElement = signElement;
            XmlElement datetimeElement = QuickSelectUtil.SearchChildElementByLocalName(signElement, "UntrustedSystemDateTime");
            if (datetimeElement != null)
            {
                DateTime dateTime = DateTime.ParseExact(datetimeElement.InnerText.Trim(), "yyyy-MM-ddTHH:mm:ssZ", System.Globalization.CultureInfo.InvariantCulture);
                elementInfo.SignedOn = dateTime.ToString();
            }
            X509Certificate2 cert2 = null;
            SignedXml sXml = null;
            try
            {
                sXml = new SignedXml(signElement.OwnerDocument);
                sXml.LoadXml(signElement);

                XmlElement certElement = QuickSelectUtil.SelectChildElementByLocalNameSteps(signElement, new string[] { "KeyInfo", "X509Data", "X509Certificate" });
                if (certElement == null)
                {
                    elementInfo.Status = SignedStatus.Error;
                }
                else
                {
                    /*
                    IEnumerator keyEnum = sXml.Signature.KeyInfo.GetEnumerator();
                    keyEnum.MoveNext();
                    KeyInfoX509Data x509data = keyEnum.Current as KeyInfoX509Data;
                    cert2 = x509data.Certificates[0] as X509Certificate2;
                    */
                    byte[] certData = Convert.FromBase64String(certElement.InnerText);
                    cert2 = new X509Certificate2(certData);
                }
            }
            catch (Exception exc)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            if (cert2 != null)
            {
                //CID 1028 //2011.11.18 //Begin
                elementInfo.SignedBy = GetSimpleName(cert2.Subject);
                //CID 1028 //2011.11.18 //End

                //CID 1041 //2012.01.06 //Begin, true
                bool valid = sXml.CheckSignature(cert2, true);
                if (valid)
                {
                    try
                    {
                        X509Chain x509Chain = new X509Chain();
                        x509Chain.Build(cert2);
                        foreach (X509ChainElement chainElement in x509Chain.ChainElements)
                        {
                            bool verified = chainElement.Certificate.Verify();
                            if (verified)
                            {
                                elementInfo.Status = SignedStatus.Valid;
                                continue;
                            }
                            else
                            {
                                bool foundStatus = false;
                                foreach (X509ChainStatus chainStatus in chainElement.ChainElementStatus)
                                {
                                    X509ChainStatusFlags status = chainStatus.Status;
                                    if (status == X509ChainStatusFlags.NotTimeValid)
                                    {
                                        elementInfo.Status = SignedStatus.Expired;
                                        foundStatus = true;
                                        break;
                                    }
                                    else if (status == X509ChainStatusFlags.Revoked)
                                    {
                                        elementInfo.Status = SignedStatus.Revoked;
                                        foundStatus = true;
                                        break;
                                    }
                                    else if (chainStatus.Status == X509ChainStatusFlags.NotSignatureValid)
                                    {
                                        elementInfo.Status = SignedStatus.Invalid;
                                        foundStatus = true;
                                        break;
                                    }
                                    else if (chainStatus.Status == X509ChainStatusFlags.UntrustedRoot)
                                    {
                                        elementInfo.Status = SignedStatus.NotTrusted;
                                        foundStatus = true;
                                        break;
                                    }
                                    else
                                    {
                                        //CID 1038 //2012.01.06 //Begin
                                        //FileLog.log("Singature Status: ");
                                        //FileLog.log(cert2.ToString());
                                        //FileLog.log(chainStatus.Status.ToString());
                                        //FileLog.log(chainStatus.StatusInformation);
                                        //CID 1038 //2012.01.06 //End
                                    }
                                }
                                //CID 1038 //2012.01.06 //Begin
                                if (!foundStatus)
                                {
                                    elementInfo.Status = SignedStatus.Error;
                                }
                                //CID 1038 //2012.01.06 //End
                            }//end verified
                        }//end foreach ChainElements
                    }
                    catch (Exception ex)
                    {
                        FileLog.log("Singature Invalid: ");
                        FileLog.log(ex);
                        elementInfo.Status = SignedStatus.Invalid;
                    }
                }
                else
                {
                    elementInfo.Status = SignedStatus.Invalid;
                }
                //CID 1041 //2012.01.06 //End
            }
            else
            {
                elementInfo.SignedBy = "";
            }
            //CID 1021 //2011.11.22 //Begin
            XmlElement signedInfoElement = QuickSelectUtil.SelectChildElementByLocalName(signElement, "SignedInfo");
            if (signedInfoElement == null)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            if (QuickSelectUtil.SelectChildElementByLocalName(signElement, "SignatureValue") == null)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            if (QuickSelectUtil.SelectChildElementByLocalName(signElement, "KeyInfo") == null)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            XmlElement objectElement = QuickSelectUtil.SelectChildElementByLocalName(signElement, "Object");
            if (objectElement == null)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            List<XmlElement> referenceElements = QuickSelectUtil.SelectChildElementsByLocalName(signedInfoElement, "Reference");
            if (referenceElements.Count < 2)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            XmlElement reference2Element = referenceElements[1];
            XmlElement digestMethodElement = QuickSelectUtil.SelectChildElementByLocalName(reference2Element, "DigestMethod");
            if (digestMethodElement == null)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            string algorithm = digestMethodElement.GetAttribute("Algorithm");
            if (!algorithm.Equals("http://www.w3.org/2000/09/xmldsig#sha1"))
            {
                elementInfo.Status = SignedStatus.Error;
            }
            XmlElement digestValueElement = QuickSelectUtil.SelectChildElementByLocalName(reference2Element, "DigestValue");
            if (digestValueElement == null)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            if (digestValueElement.InnerText.Length <= 0)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            XmlElement commentElement = QuickSelectUtil.SelectChildElementByLocalNameSteps(objectElement, new string[] { "SignatureProperties", "SignatureProperty", "Comment" });
            if (commentElement == null)
            {
                elementInfo.Status = SignedStatus.Error;
            }
            if (mode == "countersign")
            { 
                XmlElement referenceElement = referenceElements[0];
                List<XmlElement> transformElements = QuickSelectUtil.SelectChildElementsByLocalNameSteps(referenceElement, new string[] { "Transforms", "Transform" });
                if (transformElements.Count < 2)
                {
                    elementInfo.Status = SignedStatus.Error;
                }
                XmlElement tranformElement2 = transformElements[1];
                string algorithm2 = tranformElement2.GetAttribute("Algorithm");
                if (!algorithm2.Equals("http://www.w3.org/TR/1999/REC-xslt-19991116"))
                {
                    elementInfo.Status = SignedStatus.Error;
                }
                //CID 1021 //2012.01.06 //Begin
                List<XmlElement> templateElements = QuickSelectUtil.SelectChildElementsByLocalNameSteps(tranformElement2, new string[] { "stylesheet", "template" });
                if (templateElements.Count < 5)
                {
                    elementInfo.Status = SignedStatus.Error;
                }
                //CID 1021 //2012.01.06 //End
            }
            //CID 1021 //2011.11.22 //End
            return elementInfo;
        }
        //CID 1021 //2011.09.29 //End

        //CID 1028 //2011.11.18 //Begin
        private static string GetSimpleName(string name)
        {
            //CID 1037 //2012.01.03 //Begin
            int index = name.IndexOf("CN=");
            int start = index + 3;
            string simpleName = "";
            if (name.IndexOf("CN=\"") == index)
            {
                int end = name.IndexOf('"', start + 2);
                start = start + 1;
                int length = end - start;
                simpleName = name.Substring(start, length);
            }
            else
            {
                int end = name.IndexOf(',', start);
                if (end < 0)
                {
                    end = name.Length;
                }
                simpleName = name.Substring(start, end - start);
            }
            return simpleName;
            //CID 1037 //2012.01.03 //End
        }
        //CID 1028 //2011.11.18 //End
    }

}
//CID 0749 //2011.09.01 //End